Array
A comprehensive guide to mastering array patterns and techniques for Data Structures and Algorithms interviews and competitive programming.
Table of Contents
- Introduction
- Basic Array Operations
- Two Pointer Techniques
- Sliding Window Patterns
- Prefix Sum Techniques
- Sorting and Searching
- Subarray Patterns
- Matrix Techniques
- Dynamic Programming on Arrays
- Advanced Patterns
- Problem-Solving Framework
- Practice Problems
Introduction
Arrays are the fundamental data structure in programming, forming the backbone of most algorithms. They're essential for:
- Sequential data processing and iteration
- Index-based access and manipulation
- Sorting and searching algorithms
- Dynamic programming solutions
- Matrix operations and 2D problems
- Sliding window and subarray techniques
When to Use Arrays
✅ Use when you need:
- Fast random access by index O(1)
- Cache-friendly sequential processing
- Fixed-size collections with known bounds
- Mathematical operations on sequences
- Sorting and searching operations
❌ Consider alternatives when:
- Frequent insertions/deletions in middle
- Unknown or highly variable size
- Need key-value associations (use objects/maps)
- Require constant-time insertion/deletion
Basic Array Operations
Core Array Methods
// Creation and initialization
const arr = new Array(5).fill(0); // [0, 0, 0, 0, 0]
const arr2 = Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]
const arr3 = [...Array(5).keys()]; // [0, 1, 2, 3, 4]
// Access and modification
arr[0] = 10; // O(1) access
const first = arr[0]; // O(1) retrieval
arr.length; // Get size
// Adding elements
arr.push(6); // Add to end - O(1) amortized
arr.unshift(0); // Add to beginning - O(n)
arr.splice(2, 0, 'new'); // Insert at index 2 - O(n)
// Removing elements
arr.pop(); // Remove from end - O(1)
arr.shift(); // Remove from beginning - O(n)
arr.splice(2, 1); // Remove at index 2 - O(n)
// Searching
arr.indexOf(value); // Find first index - O(n)
arr.lastIndexOf(value); // Find last index - O(n)
arr.includes(value); // Check if exists - O(n)
arr.find(x => x > 5); // Find first match - O(n)
arr.findIndex(x => x > 5); // Find index of first match - O(n)
Array Iteration Patterns
const numbers = [1, 2, 3, 4, 5];
// Basic iteration
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
// Functional iteration
numbers.forEach((num, index) => console.log(num, index));
numbers.map(x => x * 2); // Transform: [2, 4, 6, 8, 10]
numbers.filter(x => x % 2 === 0); // Filter: [2, 4]
numbers.reduce((sum, x) => sum + x, 0); // Reduce: 15
// Advanced iteration
for (const num of numbers) console.log(num); // Values
for (const index in numbers) console.log(index); // Indices
for (const [i, num] of numbers.entries()) {
// Index + value
console.log(i, num);
}
Time Complexity Summary:
- Access: O(1)
- Search: O(n)
- Insertion: O(1) at end, O(n) elsewhere
- Deletion: O(1) at end, O(n) elsewhere
Two Pointer Techniques
1. Opposite Direction Pointers
Problem: Two Sum in sorted array.
function twoSum(numbers, target) {
let left = 0;
let right = numbers.length - 1;
while (left < right) {
const sum = numbers[left] + numbers[right];
if (sum === target) {
return [left, right];
} else if (sum < target) {
left++;
} else {
right--;
}
}
return [];
}
Time Complexity: O(n) | Space Complexity: O(1)
2. Same Direction Pointers (Fast/Slow)
Problem: Remove duplicates from sorted array in-place.
function removeDuplicates(nums) {
if (nums.length <= 1) return nums.length;
let slow = 0; // Points to last unique element
for (let fast = 1; fast < nums.length; fast++) {
if (nums[fast] !== nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1; // New length
}
3. Three Sum Pattern
Problem: Find all unique triplets that sum to zero.
function threeSum(nums) {
nums.sort((a, b) => a - b);
const result = [];
for (let i = 0; i < nums.length - 2; i++) {
// Skip duplicates for first number
if (i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1;
let right = nums.length - 1;
const target = -nums[i];
while (left < right) {
const sum = nums[left] + nums[right];
if (sum === target) {
result.push([nums[i], nums[left], nums[right]]);
// Skip duplicates
while (left < right && nums[left] === nums[left + 1]) left++;
while (left < right && nums[right] === nums[right - 1]) right--;
left++;
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
return result;
}
4. Container With Most Water
Problem: Find maximum area between two lines.
function maxArea(height) {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
const water = width * minHeight;
maxWater = Math.max(maxWater, water);
// Move pointer with smaller height
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxWater;
}
Key Insight: Always move the pointer with smaller height to potentially find larger area.
Sliding Window Patterns
1. Fixed Size Window
Problem: Maximum sum of k consecutive elements.
function maxSumSubarray(arr, k) {
if (arr.length < k) return 0;
// Calculate sum of first window
let windowSum = 0;
for (let i = 0; i < k; i++) {
windowSum += arr[i];
}
let maxSum = windowSum;
// Slide the window
for (let i = k; i < arr.length; i++) {
windowSum += arr[i] - arr[i - k];
maxSum = Math.max(maxSum, windowSum);
}
return maxSum;
}
Time Complexity: O(n) | Space Complexity: O(1)
2. Variable Size Window
Problem: Smallest subarray with sum ≥ target.
function minSubArrayLen(target, nums) {
let left = 0;
let sum = 0;
let minLen = Infinity;
for (let right = 0; right < nums.length; right++) {
sum += nums[right];
// Shrink window while sum >= target
while (sum >= target) {
minLen = Math.min(minLen, right - left + 1);
sum -= nums[left];
left++;
}
}
return minLen === Infinity ? 0 : minLen;
}
3. Longest Substring Without Repeating Characters
Problem: Find length of longest substring with unique characters.
function lengthOfLongestSubstring(s) {
const charSet = new Set();
let left = 0;
let maxLen = 0;
for (let right = 0; right < s.length; right++) {
// Shrink window until no duplicates
while (charSet.has(s[right])) {
charSet.delete(s[left]);
left++;
}
charSet.add(s[right]);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
4. Sliding Window Maximum
Problem: Maximum in each sliding window of size k.
function maxSlidingWindow(nums, k) {
const result = [];
const deque = []; // Stores indices in decreasing order of values
for (let i = 0; i < nums.length; i++) {
// Remove indices outside current window
while (deque.length > 0 && deque[0] <= i - k) {
deque.shift();
}
// Remove indices with smaller values
while (deque.length > 0 && nums[deque[deque.length - 1]] <= nums[i]) {
deque.pop();
}
deque.push(i);
// Add maximum to result when window is complete
if (i >= k - 1) {
result.push(nums[deque[0]]);
}
}
return result;
}
Time Complexity: O(n) | Space Complexity: O(k)
Prefix Sum Techniques
1. Basic Prefix Sum
Problem: Range sum queries in O(1) time.
class PrefixSum {
constructor(nums) {
this.prefixSum = new Array(nums.length + 1).fill(0);
// Build prefix sum array
for (let i = 0; i < nums.length; i++) {
this.prefixSum[i + 1] = this.prefixSum[i] + nums[i];
}
}
// Sum of elements from index left to right (inclusive)
rangeSum(left, right) {
return this.prefixSum[right + 1] - this.prefixSum[left];
}
}
Usage:
const ps = new PrefixSum([1, 2, 3, 4, 5]);
console.log(ps.rangeSum(1, 3)); // Sum of [2, 3, 4] = 9
2. Subarray Sum Equals K
Problem: Count subarrays with sum equal to k.
function subarraySum(nums, k) {
const prefixSumCount = new Map();
prefixSumCount.set(0, 1); // Handle subarrays starting from index 0
let count = 0;
let prefixSum = 0;
for (const num of nums) {
prefixSum += num;
// Check if (prefixSum - k) exists
if (prefixSumCount.has(prefixSum - k)) {
count += prefixSumCount.get(prefixSum - k);
}
// Update prefix sum count
prefixSumCount.set(prefixSum, (prefixSumCount.get(prefixSum) || 0) + 1);
}
return count;
}
Key Insight: If prefixSum[j] - prefixSum[i] = k, then subarray from i+1 to j has sum k.
3. Maximum Subarray Sum (Kadane's Algorithm)
Problem: Find contiguous subarray with maximum sum.
function maxSubArray(nums) {
let maxSoFar = nums[0];
let maxEndingHere = nums[0];
for (let i = 1; i < nums.length; i++) {
// Either start new subarray or extend current one
maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]);
maxSoFar = Math.max(maxSoFar, maxEndingHere);
}
return maxSoFar;
}
// Variant: Return the actual subarray
function maxSubArrayWithIndices(nums) {
let maxSum = nums[0];
let currentSum = nums[0];
let start = 0,
end = 0,
tempStart = 0;
for (let i = 1; i < nums.length; i++) {
if (currentSum < 0) {
currentSum = nums[i];
tempStart = i;
} else {
currentSum += nums[i];
}
if (currentSum > maxSum) {
maxSum = currentSum;
start = tempStart;
end = i;
}
}
return {
maxSum,
subarray: nums.slice(start, end + 1),
indices: [start, end],
};
}
4. Product of Array Except Self
Problem: Return array where each element is product of all other elements.
function productExceptSelf(nums) {
const result = new Array(nums.length);
// Left pass: product of all elements to the left
result[0] = 1;
for (let i = 1; i < nums.length; i++) {
result[i] = result[i - 1] * nums[i - 1];
}
// Right pass: multiply by product of all elements to the right
let rightProduct = 1;
for (let i = nums.length - 1; i >= 0; i--) {
result[i] *= rightProduct;
rightProduct *= nums[i];
}
return result;
}
Time Complexity: O(n) | Space Complexity: O(1) excluding output array
Sorting and Searching
1. Binary Search Variations
Basic Binary Search
function binarySearch(nums, target) {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1; // Not found
}
Find First/Last Occurrence
function searchRange(nums, target) {
const findFirst = (nums, target) => {
let left = 0,
right = nums.length - 1;
let result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
result = mid;
right = mid - 1; // Continue searching left
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
};
const findLast = (nums, target) => {
let left = 0,
right = nums.length - 1;
let result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
result = mid;
left = mid + 1; // Continue searching right
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
};
return [findFirst(nums, target), findLast(nums, target)];
}
Search in Rotated Sorted Array
function searchRotated(nums, target) {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
}
// Determine which half is sorted
if (nums[left] <= nums[mid]) {
// Left half is sorted
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
// Right half is sorted
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}